
from blinker import Signal
from src.Core.Common import *
from src.Core.Common import SingletonDecorator
from src.Core.GraphManager import GraphManagerSingleton
from src.ConfigManager import ConfigManager


class _EditorState(object):
    """
    表示编辑器的一个历史状态。
    该类用于存储编辑器在某个时间点的状态信息，包括状态描述文本和序列化后的编辑器状态数据。
    """

    def __init__(self, text, modify):
        """
        初始化编辑器状态。

        :param text: 状态描述文本，用于标识该状态
        :param modify: 布尔值，指示该状态是否修改了编辑器的数据
        """
        super(_EditorState, self).__init__()
        # 存储状态描述文本
        self.text = text
        # 调用图管理器单例的 serialize 方法，获取当前编辑器的序列化状态
        self.editorState = GraphManagerSingleton().get().serialize()
        # 存储该状态是否修改数据的标志
        self._modify = modify

    def modifiesData(self):
        """
        判断该状态是否修改了编辑器的数据。

        :return: 布尔值，如果修改了数据返回 True，否则返回 False
        """
        return self._modify

    def __repr__(self):
        """
        返回状态的描述文本，方便调试和打印输出。

        :return: 状态描述文本
        """
        return self.text


@SingletonDecorator
class EditorHistory(object):
    """
    编辑器历史记录管理器，使用单例模式确保全局只有一个实例。
    该类用于管理编辑器的历史状态，支持状态的保存、撤销、重做等操作。
    """

    def __init__(self, app):
        """
        初始化编辑器历史记录管理器。

        :param app: 编辑器应用程序实例，用于加载和保存编辑器状态
        """
        # 定义信号，当有新状态被推入时发出该信号，携带新状态对象
        self.statePushed = Signal(object)
        # 定义信号，当有状态被移除时发出该信号，携带被移除的状态对象
        self.stateRemoved = Signal(object)
        # 定义信号，当选择某个状态时发出该信号，携带被选择的状态对象
        self.stateSelected = Signal(object)

        # 保存编辑器应用程序实例
        self.app = app
        # 初始化历史状态栈，用于存储所有的编辑器历史状态
        self.stack = list()
        try:
            # 从配置文件中获取历史记录的最大容量
            self._capacity = int(
                ConfigManager().getPrefsValue("PREFS", "General/HistoryDepth")
            )
        except:
            # 如果获取失败，将历史记录的最大容量默认设置为 10
            self._capacity = 10

        # 当前活动的状态，初始化为 None
        self.activeState = None

    def shutdown(self):
        """
        关闭编辑器历史记录管理器。
        断开所有信号的连接，并清空历史状态栈。
        """
        clearSignal(self.statePushed)
        clearSignal(self.stateRemoved)
        clearSignal(self.stateSelected)
        clearList(self.stack)

    def getStack(self):
        """
        获取历史状态栈。

        :return: 存储所有编辑器历史状态的列表
        """
        return self.stack

    def count(self):
        """
        获取历史状态栈中状态的数量。

        :return: 历史状态栈中状态的数量
        """
        return len(self.stack)

    @property
    def capacity(self):
        """
        获取历史状态栈的最大容量。

        :return: 历史状态栈的最大容量
        """
        return self._capacity

    @capacity.setter
    def capacity(self, value):
        """
        设置历史状态栈的最大容量。
        如果新的容量小于当前栈的大小，会移除栈顶的多余状态。

        :param value: 新的历史状态栈最大容量
        """
        self._capacity = value
        if value < len(self.stack):
            for i in range(len(self.stack) - value):
                # 弹出栈顶的状态
                state = self.stack.pop()
                # 发出状态移除信号
                self.stateRemoved.send(state)

    def clear(self):
        """
        清空历史状态栈。
        """
        clearList(self.stack)

    def stateIndex(self, state):
        """
        获取指定状态在历史状态栈中的索引。

        :param state: 要查找的编辑器历史状态对象
        :return: 状态在栈中的索引，如果状态不存在则返回 -1
        """
        if state in self.stack:
            return self.stack.index(state)
        return -1

    @property
    def currentIndex(self):
        """
        获取当前活动状态在历史状态栈中的索引。

        :return: 当前活动状态的索引，如果没有活动状态则返回 -1
        """
        if self.activeState is not None:
            return self.stateIndex(self.activeState)
        return -1

    def push(self, edState):
        """
        向历史状态栈中添加一个新的状态。
        如果当前活动状态不是栈顶元素，会移除当前活动状态之后的所有状态。
        如果栈的大小超过最大容量，会移除栈底的状态。

        :param edState: 要添加的编辑器历史状态对象
        """
        if self.currentIndex < self.count() - 1:
            while True:
                index = self.count() - 1
                nextState = self.stack[index]
                if nextState == self.activeState:
                    break
                # 弹出当前活动状态之后的状态
                state = self.stack.pop()
                # 发出状态移除信号
                self.stateRemoved.send(state)

        # 将新状态添加到栈顶
        self.stack.append(edState)

        if len(self.stack) >= self.capacity:
            # 当栈的大小超过最大容量时，移除栈底的状态
            poppedState = self.stack.pop(0)
            # 发出状态移除信号
            self.stateRemoved.send(poppedState)

        # 发出新状态推入信号
        self.statePushed.send(edState)
        # 将新状态设置为当前活动状态
        self.activeState = edState
        # 发出状态选择信号
        self.stateSelected.send(edState)

    def selectState(self, state):
        """
        选择指定的编辑器历史状态，将编辑器恢复到该状态。

        :param state: 要选择的编辑器历史状态对象
        """
        for st in self.stack:
            if state == st:
                # 调用应用程序的 loadFromData 方法，将编辑器恢复到该状态
                self.app.loadFromData(st.editorState)
                # 将该状态设置为当前活动状态
                self.activeState = st
                # 发出状态选择信号
                self.stateSelected.send(st)
                break

    def select(self, index):
        """
        根据索引选择编辑器历史状态，将编辑器恢复到该状态。
        会对索引进行边界检查，确保索引在有效范围内。

        :param index: 要选择的状态的索引
        """
        # 对索引进行边界检查，确保索引在 0 到栈大小减 1 的范围内
        index = clamp(index, 0, self.count() - 1)

        if index == self.currentIndex:
            return

        if len(self.stack) == 0:
            return

        # 获取指定索引的状态的序列化数据
        stateData = self.stack[index].editorState

        # 调用应用程序的 loadFromData 方法，将编辑器恢复到该状态
        self.app.loadFromData(stateData)

        # 获取指定索引的状态对象
        state = self.stack[index]
        # 将该状态设置为当前活动状态
        self.activeState = state
        # 发出状态选择信号
        self.stateSelected.send(state)

    def saveState(self, text, modify=False):
        """
        保存一个新的编辑器历史状态。

        :param text: 状态描述文本，用于标识该状态
        :param modify: 布尔值，指示该状态是否修改了编辑器的数据，默认为 False
        """
        # 创建一个新的编辑器状态对象并添加到历史状态栈中
        self.push(_EditorState(text, modify))

    def undo(self):
        """
        执行撤销操作，将编辑器恢复到上一个历史状态。
        """
        if self.currentIndex > 0:
            # 选择当前活动状态的前一个状态
            self.select(self.currentIndex - 1)

    def redo(self):
        """
        执行重做操作，将编辑器恢复到下一个历史状态。
        """
        self.select(self.currentIndex + 1)